package org.onionframework.faces.web.action;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.apache.commons.lang.StringUtils;
import org.onionframework.core.util.Reflections;
import org.onionframework.faces.web.action.support.CommonDataTableBean;
import org.onionframework.faces.web.action.support.FacesMessages;
import org.onionframework.faces.web.action.support.FormPanel;
import org.onionframework.faces.web.action.support.PageQuery;
import org.onionframework.persistence.extend.ExtendComponentUtils;
import org.onionframework.persistence.model.AbstractModel;
import org.onionframework.persistence.model.Model;
import org.onionframework.persistence.model.annotation.DomainModel;
import org.onionframework.persistence.model.logic.Extendable;
import org.onionframework.persistence.model.logic.Named;
import org.onionframework.persistence.model.support.ExtendComponent;
import org.onionframework.persistence.query.OnionQuery;
import org.onionframework.persistence.service.GenericService;
import org.onionframework.servlet.ServletContexts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

/**
 * Title:jsf顶级action类<br>
 * Description:与页面进行交互的逻辑类<br>
 * Company: 亚信联创集团股份有限公司<br>
 * 
 * @author author
 * @see
 * @CreateDate 2011-8-30 下午04:39:55
 * 
 */
public class GenericAction<T extends Model<? extends Serializable>> implements
		Serializable {

	/** 序列化序号 */
	private static final long serialVersionUID = 5689222249030856717L;

	protected List<T> datas; // 用于列表展现的数据

	/** 与数据库交互的实体对象,一般对象数据库中的某张表 */
	protected T entity; // 用于编辑、展示的实体对象

	/** 基本的业务逻辑类 */
	protected GenericService<T> service; // 服务类

	protected Class<T> entityClass = Reflections.getClassGenricType(this.getClass());
	
	protected CommonDataTableBean dataTable; // 实现行选择的辅助类

	/** 查询条件 */
	protected Map<String, Object> queryParams = new HashMap<String, Object>();
	
	protected Map<String, Object> pageParams = new HashMap<String, Object>();
	
	public static final String PARAM_NAME_ID = "id";
	
	public boolean form = false;	//不经过列表，直接打开表单时为true
	
	private ExtendComponent ex;

	public GenericAction() {
	}

	public GenericAction(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	/**
	 * 初始化方法，bean创建后调用
	 */
	@PostConstruct
	public void init() {
		this.form = Boolean.valueOf(ServletContexts.getParameter("form"));
		if(form){
			String id = ServletContexts.getParameter("id");
			this.editEntity(this.loadEntity(id));
		}else{
			this.initPageParams();
			this.initDatas(this.createOnionQuery());
		}
	}
	
	/**
	 * 将URL参数中的query_开头的参数初始化到queryParams中
	 */
	private void initPageParams() {
		this.pageParams.clear();
		Enumeration<String> names = ServletContexts.getRequest().getParameterNames();
		String name;
		while(names.hasMoreElements()){
			name = names.nextElement();
			this.pageParams.put(name, ServletContexts.getParameter(name));
		}
	}

	/**
	 * 直接打开表单时加载id指定的对象
	 * @param id 编辑的对象的id
	 */
	protected T loadEntity(String id){
		return this.service.load(id);
	}

	/**
	 * 创建业务对象
	 */
	protected T createEntity() {
		return this.service.create();
	}

	/**
	 * 处理新建的Action方法，创建业务对象，初始化模态框
	 * 
	 * @param viewId
	 *            表单页面位置
	 */
	public void create(String viewId) {
		this.entity = this.createEntity();
		this.readEx(this.entity);
		this.initFormPanel(viewId);
		this.onEdit();
	}

	/**
	 * 编辑、打开记录的时候调用，这个方法一般不会在页面上调用
	 * 
	 * @param object
	 *            要编辑的对象
	 */
	public void editEntity(T object) {
		this.entity = object;
		this.onEdit();
		this.readEx(object);
	}

	/**
	 * 编辑当前选中记录，使用指定的视图
	 * 
	 * @param viewId
	 */
	public void edit(String viewId) {
		this.editEntity(this.getSelected());
		this.initFormPanel(viewId);
	}


	protected void initFormPanel(String viewId) {
		FormPanel.init(viewId);
	}

	/**
	 * 将经过编辑的扩展属性对象设置到业务模型对象上
	 * 
	 * 在保存之前调用，见save方法
	 */
	protected void writeEx(T object){
		if(object instanceof Extendable){
			Extendable extendable = (Extendable)object;
			extendable.setEx(this.ex);
		}
	}
	
	/**
	 * 读取模型上的扩展属性，设置到当前action的ex属性上
	 * 如果模型上得扩展属性是null，那么需要创建新的扩展
	 * 对象
	 * 
	 * 在打开记录的时候调用，见edit方法
	 */
	@SuppressWarnings("unchecked")
	protected void readEx(T object){
		if(object instanceof Extendable){
			Extendable extendable = (Extendable)object;
			if(extendable.getEx() != null)
				this.ex = extendable.getEx();
			else
				this.ex = ExtendComponentUtils.create((Class<? extends Extendable>)this.entityClass);
		}else
			this.ex = null;
	}
	
	/*public void export(UIData component, String type) {
		ServletContexts.getRequest().setAttribute(
				ExcelExporter.EXPORT_COMPONENT_KEY, component);
		ServletContexts.getRequest().setAttribute(
				ExcelExporter.EXPORT_TYPE_KEY, type);
	}*/

	/**
	 * 在编辑、新增的时候的回调方法，一般实现表单额外数据（例如下拉框需要的数据）初始化工作
	 */
	protected void onEdit() {
	}

	/**
	 * 获取当前数据表格中选中的数据条目
	 */
	@SuppressWarnings("unchecked")
	protected T getSelected() {
		Object selectedValue = this.dataTable.getSelected();
		
		if(selectedValue == null)
			return null;
		
		if (this.entityClass.isAssignableFrom(selectedValue.getClass())){
			return (T)selectedValue;
		}
		else if (selectedValue instanceof Map) {
			Map<String, Object> map = (HashMap<String, Object>) selectedValue;
			if (map.containsKey("id"))
				return this.service.get((Serializable) map.get("id"));
			else if (map.containsKey("ID"))
				return this.service.get((Serializable) map.get("ID"));
			else
				throw new RuntimeException("找不到对象标识");
		} else
			throw new RuntimeException("无法判断选中的记录的类型");
	}

	/**
	 * 获取对象标题，格式为“对象类型-对象名称”
	 * 
	 * @param o
	 * @return
	 */
	protected String getObjectTitle(Object o) {
		Assert.notNull(o);
		StringBuilder title = new StringBuilder();
		DomainModel domainModel = o.getClass().getAnnotation(DomainModel.class);
		if (domainModel != null && StringUtils.isNotBlank(domainModel.name()))
			title.append(domainModel.name());

		if (o instanceof Model && ((Model<?>) o).getId() == null)
			title.append("-新建");
		else if (o instanceof Named) {
			Named namedObject = (Named) o;
			if (namedObject.getName() != null)
				title.append('-').append(namedObject.getName());
		}

		return title.toString();
	}

	public void save() {
		this.writeEx(this.entity);
		this.service.save(this.entity);
		FacesMessages.addMessage("保存成功");
		this.queryParams.clear();
		this.refresh();
	}

	public void delete(T object) {
		this.service.remove(object);
	}

	public void deleteById(Serializable id) {
		this.service.removeById(id);
	}

	/**
	 * 多选删除
	 */
	@SuppressWarnings("unchecked")
	public void delete() {
		List<?> selections = this.dataTable.getSelections();
		for (Object o : selections) {
			if (this.entityClass.isAssignableFrom(o.getClass()))
				this.delete((T) o);
			else if (o instanceof Map) {
				Map<String, Object> map = (HashMap<String, Object>) o;
				Serializable id = (Serializable) map.get("id");
				if (id == null)
					id = (Serializable) map.get("ID");
				this.deleteById(id);
			}
		}
		FacesMessages.addMessage("删除成功");
		this.refresh();
	}

	/**
	 * 查询（点击查询按钮的时候执行此方法）
	 */
	public void query() {
		this.initDatasWithQuery(this.createOnionQuery());
	}

	/**
	 * 刷新（点击刷新按钮的时候执行此方法）
	 * 
	 * 另外在执行某些操作导致需要刷新Action内的数据模型的时候也是用这个方法， 例如在保存之后
	 */
	public void refresh() {
		if (this.queryParams.isEmpty())
			this.initDatas(this.createOnionQuery());
		else
			this.query();
	}

	/**
	 * 加入查询条件
	 */
	protected void initDatasWithQuery(OnionQuery onionQuery) {

		PageQuery query = this.getQuery();

		if (query != null) {
			if (query.getOnionQuery() != null) {
				onionQuery.addAll(query.getOnionQuery());
			}
		}

		this.initDatas(onionQuery);
	}

	/**
	 * 初始化数据，页面上有查询的action可以覆盖这个方法
	 * 
	 * @param onionQuery
	 * @param sortProperties
	 */
	protected void initDatas(OnionQuery onionQuery) {
		this.datas = this.service.find(onionQuery);
	}

	protected PageQuery getQuery() {
		if (this.queryParams.isEmpty())
			return null;
		else
			return new PageQuery(this.queryParams, this.entityClass);
	}
	
	/**
	 * 创建在初始化、刷新、查询等场景下要用的OnionQuery
	 */
	protected OnionQuery createOnionQuery(){
		OnionQuery query;
		if(this.pageParams.isEmpty() || this.pageParams.containsKey("AJAXREQUEST"))
			query = OnionQuery.instance(this.entityClass);
		else{
			Map<String,Object> pageQueryParams = new HashMap<String,Object>();
			for(Map.Entry<String, Object> entry : this.pageParams.entrySet()){
				if(entry.getKey().startsWith("query_"))
					pageQueryParams.put(entry.getKey().substring(6), entry.getValue());
			}
			query = new PageQuery(pageQueryParams,this.entityClass).getOnionQuery();
		}
		
		if(AbstractModel.class.isAssignableFrom(this.entityClass)){
			query.desc("createTime");
		}else
			query.desc("id");
		
		return query;
	}
	
	/**
	 * 前台表单上点击充值按钮的处理方法
	 * 
	 * 对于没有ajax交互的前端表单查询区，可以简单实用input type=reset，也就和这个方法无关
	 * 对于使用了ajax的查询区，重置按钮需要是一个commandButton，调用本方法，而且子类可以覆盖做一些其他的清理逻辑
	 */
	public void resetQueryParams(){
		this.queryParams.clear();
	}

	public Object getDatas() {
		return datas;
	}

	public T getEntity() {
		return entity;
	}

	public void setEntity(T entity) {
		this.entity = entity;
	}


	public GenericService<T> getService() {
		return this.service;
	}

	public void setService(GenericService<T> service) {
		this.service = service;
	}

	/**
	 * @param dataTable
	 *            the dataTable to set
	 */
	@Autowired
	public void setDataTable(CommonDataTableBean dataTable) {
		this.dataTable = dataTable;
	}

	/**
	 * @return the queryParams
	 */
	public Map<String, Object> getQueryParams() {
		return queryParams;
	}

	/**
	 * @return the entityClass
	 */
	public Class<T> getEntityClass() {
		return entityClass;
	}

	public boolean isForm() {
		return form;
	}

	/**
	 * @return the ex
	 */
	public ExtendComponent getEx() {
		return ex;
	}
}
